﻿// This source file is part of the atomdb project
// 
// Copyright (c) 2017 - 2022 pulsarware, All rights reserved.
// Copyright (c) 2017 - 2022 polarboy <polarboy@163.com>
// 
// See https://atomdb.com/LICENSE.txt for license information
// 
// Created by polarboy on 2023/12/01.

using System.CommandLine;
using System.CommandLine.Parsing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Pulsarware.ATOMDB.Packager.Command;

namespace Pulsarware.ATOMDB.Packager;

public class StartupService
{
   private readonly ILogger<StartupService> _logger;
   private Config _config;
   private RootCommand? _rootCommand;
   private IPackager _packager;

   public StartupService(
      ILogger<StartupService> logger, 
      IOptions<Config> config,
      IPackager packager)
   {
      _logger = logger;
      _config = config.Value;
      _packager = packager;
   }

   public async Task<int> ExecuteAsync(string[] args)
   {
      PrepareCommandLineParser();
      // 对其他的不感兴趣
      return await _rootCommand!.InvokeAsync(args);
   }

   private void PrepareCommandLineParser()
   {
      _rootCommand = new RootCommand("""
                                     atomdb-packager The ATOMDB bundle generate tool
                                     Copyright Pulsarware Technologies Ltd.
                                     """);
      var qtDirOption = new Option<DirectoryInfo>("--qt-dir", "Qt SDK path");
      _rootCommand.AddOption(qtDirOption);

      var qtVersionOption = new Option<Version?>("--qt-version", "Qt SDK Version number");
      _rootCommand.AddOption(qtVersionOption);

      var sourceDirArg = new Argument<DirectoryInfo>("sourcedir", "ATOMDB project source directory");
      
      sourceDirArg.AddValidator((result) =>
      {
         var sourceDir = result.GetValueForArgument(sourceDirArg);
         if (!sourceDir.Exists)
         {
            result.ErrorMessage = $"Source directory: {sourceDir} is not exist";
         }
      });
      _rootCommand.AddArgument(sourceDirArg);

      var buildDirOption = new Option<DirectoryInfo>("--build-dir", "The workspace directory");
      sourceDirArg.AddValidator((result) =>
      {
         var buildDir = result.GetValueForOption(buildDirOption);
         if (buildDir is not null && !buildDir.Exists)
         {
            result.ErrorMessage = $"Build directory: {buildDir} is not exist";
         }
      });
      
      buildDirOption.IsRequired = true;
      _rootCommand.AddOption(buildDirOption);

      var jobsOption = new Option<int>("--jobs", "Number of tasks to run in parallel");
      _rootCommand.AddOption(jobsOption);

      var miniCMakeVersionOption = new Option<Version?>(
         name: "--min-cmake-version", 
         description: "The minimum required CMake version",
         parseArgument: ParserVersionOption);
      _rootCommand.AddOption(miniCMakeVersionOption);

      var miniIfwVersionOption = new Option<Version?>(
         name:"--min-ifw-version", 
         description:"The minimum required Qt installer framework version",
         parseArgument: ParserVersionOption);
      _rootCommand.AddOption(miniIfwVersionOption);
      
      var releaseVersionOption = new Option<Version?>(
         name: "--release-version", 
         description: "The target release version", 
         parseArgument: ParserVersionOption)
      {
         IsRequired = true
      };
      _rootCommand.AddOption(releaseVersionOption);

      var useNinjaOption = new Option<bool>("--use-ninja", "Use Ninja to compile project");
      useNinjaOption.SetDefaultValue(true);
      _rootCommand.Add(useNinjaOption);

      var showBuildTimeOption = new Option<bool>("--show-build-time", "Whether Generate build time");
      showBuildTimeOption.SetDefaultValue(true);
      _rootCommand.Add(showBuildTimeOption);

      var appendVersionToPackageFilenameOption = new Option<bool>(
         "--append-version-to-bundle", 
         "Whether append version info to bundle filename");
      appendVersionToPackageFilenameOption.SetDefaultValue(true);
      _rootCommand.AddOption(appendVersionToPackageFilenameOption);

      var appendDateToPackageFilenameOption = new Option<bool>(
         "--append-date-to-bundle",
         "Whether append time info to bundle filename");
      appendDateToPackageFilenameOption.SetDefaultValue(true);
      _rootCommand.AddOption(appendDateToPackageFilenameOption);
      
      var commandOptionBinder = new CommandOptionBinder(_config)
      {
         QtDirOption = qtDirOption,
         QtVersionOption = qtVersionOption,
         SourceDirArg = sourceDirArg,
         BuildDirOption = buildDirOption,
         JobsOption = jobsOption,
         MiniCMakeVersionOption = miniCMakeVersionOption,
         ReleaseVersionOption = releaseVersionOption!,
         UseNinjaOption = useNinjaOption,
         ShowBuildTimeOption = showBuildTimeOption,
         AppendDateToPackageFilenameOption = appendVersionToPackageFilenameOption,
         AppendVersionToPackageFilenameOption = appendVersionToPackageFilenameOption
      };
      
      _rootCommand.SetHandler(MainCommandHandler, commandOptionBinder);
   }

   private static Version? ParserVersionOption(ArgumentResult argumentResult)
   {
      if (!argumentResult.Tokens.Any())
      {
         return null;
      }

      var versionStr = argumentResult.Tokens.Single().Value;
      if (Version.TryParse(versionStr, out var version))
      {
         return version;
      }
      argumentResult.ErrorMessage = "Version option value format error.";
      return null;
   }

   private Task<int> MainCommandHandler(Config config)
   {
      SetupDefaultConfig(config);
      try
      {
         _packager.Build();
         return Task.FromResult(0);
      }
      catch (Exception exception)
      {
         Console.Error.WriteLine($"Build failed: {exception.Message}");
         return Task.FromResult(1);;
      }
   }

   private static void SetupDefaultConfig(Config config)
   {
      
      if (config.Jobs == 0)
      {
         // 设置成 CPU 线程数
         config.Jobs = Environment.ProcessorCount;
      }

      config.MiniCMakeVersion ??= new Version("3.27.7");
      config.QtVersion ??= new Version("6.6.0");
      config.MiniIfwVersion ??= new Version("4.6.0");
   }
}